use crate::protocol::{RedisCommand, RespValue};
use crate::storage::{Storage, StorageEngine};
use std::time::{SystemTime, UNIX_EPOCH};

/// DEL key [key ...]
pub async fn del(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() == 0 {
        return RespValue::error("ERR wrong number of arguments for 'del' command");
    }

    let mut deleted = 0;
    for key in &command.args {
        if storage.del(key) {
            deleted += 1;
        }
    }

    RespValue::integer(deleted)
}

/// EXISTS key [key ...]
pub async fn exists(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() == 0 {
        return RespValue::error("ERR wrong number of arguments for 'exists' command");
    }

    let mut count = 0;
    for key in &command.args {
        if storage.exists(key) {
            count += 1;
        }
    }

    RespValue::integer(count)
}

/// KEYS pattern
pub async fn keys(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'keys' command");
    }

    let pattern = &command.args[0];
    let keys = storage.keys(pattern);

    let resp_keys: Vec<RespValue> = keys.into_iter()
        .map(|k| RespValue::bulk_string(k))
        .collect();

    RespValue::array(resp_keys)
}

/// TYPE key
pub async fn type_cmd(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'type' command");
    }

    let key = &command.args[0];

    match storage.r#type(key) {
        Some(type_name) => RespValue::simple_string(type_name),
        None => RespValue::simple_string("none"),
    }
}

/// TTL key
pub async fn ttl(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'ttl' command");
    }

    let key = &command.args[0];

    match storage.ttl(key) {
        Some(ttl_seconds) => RespValue::integer(ttl_seconds),
        None => RespValue::integer(-2), // Key doesn't exist
    }
}

/// EXPIRE key seconds
pub async fn expire(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 2 {
        return RespValue::error("ERR wrong number of arguments for 'expire' command");
    }

    let key = &command.args[0];
    let seconds = match command.args[1].parse::<u64>() {
        Ok(s) => s,
        Err(_) => return RespValue::error("ERR invalid expire time"),
    };

    if storage.expire(key, seconds) {
        RespValue::integer(1)
    } else {
        RespValue::integer(0)
    }
}

/// PERSIST key
pub async fn persist(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'persist' command");
    }

    let key = &command.args[0];

    if storage.persist(key) {
        RespValue::integer(1)
    } else {
        RespValue::integer(0)
    }
}

/// FLUSHDB
pub async fn flushdb(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 0 {
        return RespValue::error("ERR wrong number of arguments for 'flushdb' command");
    }

    storage.flush_all();
    RespValue::ok()
}

/// PING [message]
pub async fn ping(_storage: &Storage, command: RedisCommand) -> RespValue {
    match command.arg_count() {
        0 => RespValue::pong(),
        1 => RespValue::bulk_string(command.args[0].clone()),
        _ => RespValue::error("ERR wrong number of arguments for 'ping' command"),
    }
}

/// ECHO message
pub async fn echo(_storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'echo' command");
    }

    RespValue::bulk_string(command.args[0].clone())
}

/// TIME
pub async fn time(_storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 0 {
        return RespValue::error("ERR wrong number of arguments for 'time' command");
    }

    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap();

    let seconds = now.as_secs();
    let microseconds = now.subsec_micros();

    RespValue::array(vec![
        RespValue::bulk_string(seconds.to_string()),
        RespValue::bulk_string(microseconds.to_string()),
    ])
}

/// INFO [section]
pub async fn info(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() > 1 {
        return RespValue::error("ERR wrong number of arguments for 'info' command");
    }

    let stats = storage.stats();

    let info = format!(
        "# Server\r\nredis_version:minidis-0.1.0\r\nprocess_id:{}\r\n\r\n# Memory\r\ntotal_keys:{}\r\nexpired_keys:{}\r\nhits:{}\r\nmisses:{}\r\n",
        std::process::id(),
        stats.total_keys,
        stats.expired_keys,
        stats.hits,
        stats.misses
    );

    RespValue::bulk_string(info)
}